package com.dynamic.redis.config;
import com.dynamic.redis.utils.EnvironmentUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.NonNull;

import java.util.Set;

public class RedisMultiDataSourceRegistrar implements EnvironmentAware, ImportBeanDefinitionRegistrar {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private Environment environment;

    @Override
    public void setEnvironment(@NonNull Environment environment) {
        this.environment = environment;
    }

    /**
     * 为每个redis数据源注入BeanDefinition
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {

        Set<String> names = EnvironmentUtil.loadRedisDataSourceName((AbstractEnvironment) environment);

        if (names.size() <= 0) {
            logger.error("no multi datasource config, inject multi datasource failed. please check config.");
            return;
        }
        logger.info("register redis datasource: {}", names);
        for (String name : names) {
            // 注册 RedisTemplate BeanDefinition
            registerRedisTemplateBeanDefinition(name, RedisTemplateFactoryBean.class, registry);
        }
    }

    /**
     * 注册 RedisTemplate BeanDefinition
     */
    protected final void registerRedisTemplateBeanDefinition(String alias, Class<?> type, BeanDefinitionRegistry registry) {
        // BeanDefinition构建器
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(type);
        // 设置通过名称注入
        builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
        builder.addConstructorArgValue(null);
        // 设置数据源的名称(即设置bean的datasource属性的值)
        builder.addPropertyValue(RedisDataSourceContext.FIELD_DATASOURCE_NAME, alias);
        // 通过构建器获取bean的定义信息
        BeanDefinition beanDefinition = builder.getBeanDefinition();
        // 设置主要的注入的对象
        beanDefinition.setPrimary(false);
        String beanName = alias + RedisTemplate.class.getSimpleName();
        System.out.println(beanName);
        // 设置该bean的名称（数据源名称 + RedisTemplate）和别名（数据源名称 + -template）
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, beanName, new String[]{alias + "-template"});
        // 注册bean定义信息
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }



    /**
     * 创建 RedisTemplate 的 FactoryBean
     * FactoryBean一般用于构建复杂的bean
     */
    protected class RedisTemplateFactoryBean extends RedisDataSourceContext implements FactoryBean<Object> {
        private final Logger logger = LoggerFactory.getLogger(getClass());
        /**
         * 返回要创建的bean对象
         */
        @Override
        public Object getObject() throws Exception {
            // 为该数据源创建一个Redis连接工厂，连向指定的数据源
            DynamicRedisTemplateFactory<String, String> dynamicRedisTemplateFactory = getDynamicRedisTemplateFactory();
            logger.info("Dynamic create a RedisTemplate named {}", getDataSourceName());
            return dynamicRedisTemplateFactory.createRedisTemplate(getRedisProperties().getDatabase());
        }
        /**
         * 通过数据源对应的Redis配置获取DynamicRedisTemplateFactory
         */
        protected DynamicRedisTemplateFactory<String, String> getDynamicRedisTemplateFactory() {
            // 获取数据源对应的Redis配置信息
            RedisProperties redisProperties = getRedisProperties();
            // 根据配置信息构建一个RedisTemplateFactory
            return new DynamicRedisTemplateFactory<>(redisProperties);
        }

        /**
         * 返回要创建的bean的类型
         */
        @Override
        public Class<?> getObjectType() {
            return RedisTemplate.class;
        }
    }

}
